//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package user_api_serv

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"atomgit.com/openlinksaas-org/api-server/api_common"
	"atomgit.com/openlinksaas-org/api-server/dao"
	"atomgit.com/openlinksaas-org/api-server/dao/user_dao"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/user_api"
	"github.com/dchest/uniuri"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/mongo"
)

func getGitCodeAccessToken(clientId, clientSecret, code string) (string, string, error) {
	query := make(url.Values)
	query.Add("grant_type", "authorization_code")
	query.Add("code", code)
	query.Add("client_id", clientId)
	query.Add("client_secret", clientSecret)

	reqUrl := fmt.Sprintf("https://gitcode.com/oauth/token?%s", query.Encode())
	res, err := http.Post(reqUrl, "application/x-www-form-urlencoded", nil)

	if err != nil {
		return "", "", err
	}
	defer res.Body.Close()
	resData, err := io.ReadAll(res.Body)
	if err != nil {
		return "", "", err
	}
	resObj := map[string]any{}
	err = json.Unmarshal(resData, &resObj)
	if err != nil {
		return "", "", err
	}
	fmt.Println(string(resData))
	//失败 {"error_code":400,"error_code_name":"BAD_REQUEST","error_message":"授权码错误","trace_id":"a79ca715786548c78ce3c79840126347"}
	//成功 {"access_token":"9ur_5dANuodHjWS-QSx7W98y","expires_in":1296000,"refresh_token":"7721a401e8dc43fb9b3d80216aeb0ba0","scope":"all_user all_key all_issue all_note all_hook all_repository all_pr","created_at":"2024-08-08T04:26:49.396Z"}

	errMsg, ok := resObj["error_message"]
	if ok {
		return "", "", fmt.Errorf(errMsg.(string))
	}
	accessToken := resObj["access_token"]
	refreshToken := resObj["refresh_token"]
	if accessToken == nil || refreshToken == nil {
		return "", "", fmt.Errorf("wrong response")
	}
	return accessToken.(string), refreshToken.(string), nil
}

func refreshGitCodeAccessToken(refreshToken string) (string, string, error) {
	query := make(url.Values)
	query.Add("grant_type", "refresh_token")
	query.Add("refresh_token", refreshToken)

	reqUrl := fmt.Sprintf("https://gitcode.com/oauth/token?%s", query.Encode())

	res, err := http.Post(reqUrl, "application/x-www-form-urlencoded", nil)
	if err != nil {
		return "", "", err
	}
	defer res.Body.Close()
	resData, err := io.ReadAll(res.Body)
	if err != nil {
		return "", "", err
	}
	resObj := map[string]any{}
	err = json.Unmarshal(resData, &resObj)
	if err != nil {
		return "", "", err
	}
	fmt.Println(string(resData))
	//失败 {"error_code":400,"error_code_name":"BAD_REQUEST","error_message":"refresh_token不存在或已过期","trace_id":"e0a9eff3192a4eecba72014d1d59677e"}

	//成功 {"access_token":"xESYAiGhp4EuWU9xCZ4kZxYR","expires_in":1296000,"refresh_token":"6ae834a6701f428f95aac1d202552af3","scope":"all_user all_key all_issue all_note all_hook all_repository all_pr","created_at":"2024-08-08T04:32:17.005Z"}

	errMsg, ok := resObj["error_message"]
	if ok {
		return "", "", fmt.Errorf(errMsg.(string))
	}
	accessToken := resObj["access_token"]
	newRefreshToken := resObj["refresh_token"]
	if accessToken == nil || newRefreshToken == nil {
		return "", "", fmt.Errorf("wrong response")
	}
	return accessToken.(string), newRefreshToken.(string), nil
}

func getGitCodeUserName(accessToken string) (string, string, string, error) {
	req, err := http.NewRequest("GET", "https://api.gitcode.com/api/v5/user", nil)
	if err != nil {
		return "", "", "", err
	}
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	req.Header.Set("Authorization", fmt.Sprintf("Bearer  %s", accessToken))
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", "", "", err
	}
	defer res.Body.Close()
	resData, err := io.ReadAll(res.Body)
	if err != nil {
		return "", "", "", err
	}
	resObj := map[string]any{}
	err = json.Unmarshal(resData, &resObj)
	if err != nil {
		return "", "", "", err
	}
	fmt.Println(string(resData))
	//失败 {"error_code":404,"error_code_name":"NOT_PATH","error_message":"404, token not found"}
	//成功返回
	// {
	// 	"avatar_url": "https://cdn-img.gitcode.com/ee/bc/4e4989e374bc76aa35d49d5217bd120e1b5d0e8ac0defb05f64d2b976b6276ed.png?time=1717491324683",
	// 	"events_url": null,
	// 	"followers_url": "https://api.gitcode.com/api/v5users/linksaas/followers",
	// 	"following_url": null,
	// 	"gists_url": null,
	// 	"html_url": "https://gitcode.com/linksaas",
	// 	"id": "66543c313d25f6302e0c7a62",
	// 	"login": "linksaas",
	// 	"member_role": null,
	// 	"name": "linksaas",
	// 	"organizations_url": null,
	// 	"received_events_url": null,
	// 	"remark": null,
	// 	"repos_url": null,
	// 	"starred_url": null,
	// 	"subscriptions_url": null,
	// 	"type": "User",
	// 	"url": "https://api.gitcode.com/api/v5/linksaas",
	// 	"bio": "",
	// 	"blog": "https://www.linksaas.pro",
	// 	"company": "深圳市同心圆网络有效公司",
	// 	"created_at": null,
	// 	"email": "linksaas@noreply.gitcode.com",
	// 	"followers": 0,
	// 	"following": 0,
	// 	"profession": null,
	// 	"public_gists": null,
	// 	"public_repos": null,
	// 	"qq": null,
	// 	"stared": null,
	// 	"updated_at": null,
	// 	"watched": null,
	// 	"wechat": null,
	// 	"weibo": null
	// }
	errMsg, ok := resObj["error_message"]
	if ok {
		return "", "", "", fmt.Errorf(errMsg.(string))
	}
	login := resObj["login"]
	name := resObj["name"]
	avatarUrl := resObj["avatar_url"]
	if login == nil || name == nil || avatarUrl == nil {
		return "", "", "", fmt.Errorf("wrong response")
	}
	return "gitcode:" + login.(string), name.(string), avatarUrl.(string), nil
}

func (apiImpl *UserApiImpl) loginGitCode(ctx context.Context, req *user_api.LoginRequest) (*user_api.LoginResponse, error) {
	//获取accessToken
	accessToken, refreshToken, err := getGitCodeAccessToken(apiImpl.servCfg.GitCode.ClientId, apiImpl.servCfg.GitCode.ClientSecret, req.Passwd)
	if err != nil {
		return nil, err
	}
	//获取用户信息
	userName, displayName, logoUri, err := getGitCodeUserName(accessToken)
	if err != nil {
		return nil, err
	}
	//检查用户是否存在
	userInfoItem, err := user_dao.UserInfoDao.GetByName(ctx, userName)
	if err != nil { //用户不存在
		dbSess, err := dao.StartSession()
		if err != nil {
			return nil, err
		}
		defer dao.EndSession(dbSess)

		userId, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
			userId := uuid.NewString()
			err = api_common.CreateUser(sessCtx, userId, userName, displayName, logoUri, "", false, user_api.USER_TYPE_USER_TYPE_GIT_CODE)
			if err != nil {
				return "", err
			}
			return userId, nil
		})
		if err != nil {
			return nil, err
		}
		userInfoItem, err = user_dao.UserInfoDao.Get(ctx, userId.(string))
		if err != nil {
			return &user_api.LoginResponse{
				Code:   user_api.LoginResponse_CODE_WRONG_USERNAME,
				ErrMsg: "用户不存在",
			}, nil
		}
	} else {
		if userInfoItem.BasicInfo.DisplayName != displayName || userInfoItem.BasicInfo.LogoUri != logoUri {
			err = user_dao.UserInfoDao.UpdateBasicInfo(ctx, userInfoItem.UserId, &user_dao.BasicUserInfoDbItem{
				DisplayName: displayName,
				LogoUri:     logoUri,
			})
			if err != nil {
				return nil, err
			}
		}
	}
	userInfoItem.BasicInfo.DisplayName = displayName
	userInfoItem.BasicInfo.LogoUri = logoUri
	//创建会话
	secretItem, err := user_dao.UserSecretDao.Get(ctx, userInfoItem.UserId)
	if err != nil {
		secretItem = &user_dao.UserSecretDbItem{}
	}
	//创建会话
	sessionId := uniuri.NewLen(32) + strings.ReplaceAll(uuid.NewString(), "-", "")

	dao.CacheDao.SetSession(ctx, &dao.SessionDbItem{
		SessionId:         sessionId,
		UserId:            userInfoItem.UserId,
		UserName:          userInfoItem.UserName,
		DisplayName:       userInfoItem.BasicInfo.DisplayName,
		LogoUri:           userInfoItem.BasicInfo.LogoUri,
		UserType:          uint32(user_api.USER_TYPE_USER_TYPE_GIT_CODE),
		TestAccount:       userInfoItem.TestAccount,
		ExtraAccessToken:  accessToken,
		ExtraRefreshToken: refreshToken,
		ExtraLastRefresh:  time.Now().Unix(),
	})

	return &user_api.LoginResponse{
		Code:       user_api.LoginResponse_CODE_OK,
		SessionId:  sessionId,
		UserInfo:   userInfoItem.ToUserInfo(),
		UserSecret: secretItem.Secret,
		ExtraToken: accessToken,
	}, nil
}
